package com.rede.didiok.user.service.impl;

import java.util.*;
import java.util.concurrent.TimeUnit;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;

import com.alibaba.fastjson.TypeReference;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.rede.didiok.common.constant.conf.RedisConf;
import com.rede.didiok.common.constant.conf.SQLConf;
import com.rede.didiok.common.constant.conf.SysConf;
import com.rede.didiok.common.constant.conf.base.BaseSysConf;
import com.rede.didiok.common.constant.global.Constants;
import com.rede.didiok.common.dto.BusinessIdListDto;
import com.rede.didiok.common.dto.BusinessObjectListDto;
import com.rede.didiok.common.dto.admin.LinkDto;
import com.rede.didiok.common.dto.rank.MemberGradeDto;
import com.rede.didiok.common.dto.rank.RankCategoryDto;
import com.rede.didiok.common.dto.rank.RankDto;
import com.rede.didiok.common.dto.rank.RankMemberDto;
import com.rede.didiok.common.dto.user.WebVisitDto;
import com.rede.didiok.common.enums.BehaviorEnum;
import com.rede.didiok.common.enums.ResourceTypeEnum;
import com.rede.didiok.common.enums.StatusEnum;
import com.rede.didiok.common.exception.exceptionType.FeignException;
import com.rede.didiok.common.feign.AdminFeignClient;
import com.rede.didiok.common.feign.RankFeignClient;
import com.rede.didiok.common.utils.*;
import com.rede.didiok.user.dao.WebVisitDao;
import com.rede.didiok.user.entity.UserEntity;
import com.rede.didiok.user.entity.WebVisitEntity;
import com.rede.didiok.user.service.UserService;
import com.rede.didiok.user.service.WebVisitService;


@Service("webVisitService")
public class WebVisitServiceImpl extends ServiceImpl<WebVisitDao, WebVisitEntity> implements WebVisitService {

    @Resource
    private UserService userService;

    @Resource
    private WebVisitDao webVisitDao;

    @Resource
    private RankFeignClient rankFeignClient;

    @Resource
    private AdminFeignClient adminFeignClient;

    @Autowired
    private RedisUtil redisUtil;

    @Resource
    private StringRedisTemplate stringRedisTemplate;

    /***************************** didiok-admin:start **************************/
    // region  didiok-admin

    /**
     * 获取今日网站访问人数
     *
     * @return
     */
    @Override
    public Integer getWebVisitCount() {
        // 获取今日开始和结束时间
        String startTime = DateUtils.getToDayStartTime();
        String endTime = DateUtils.getToDayEndTime();
        return webVisitDao.getIpCount(startTime, endTime);
    }

    /**
     * 获取近七天的访问量
     *
     * @return {
     * date: ["2019-6-20","2019-6-21","2019-6-22","2019-6-23","2019-6-24",,"2019-6-25","2019-6-26"]
     * pv: [10,5,6,7,5,3,2]
     * uv: [5,3,4,4,5,2,1]
     * }
     * 注：PV表示访问量   UV表示独立用户数
     */
    @Override
    public Map<String, Object> getWebVisitByWeek() {
        // 从Redis中获取一周访问量
        String weekVisitJson = redisUtil.get(RedisConf.DASHBOARD + Constants.SYMBOL_COLON + RedisConf.WEEK_VISIT);
        if (StringUtils.isNotEmpty(weekVisitJson)) {
            Map<String, Object> weekVisitMap = JsonUtils.jsonToMap(weekVisitJson);
            return weekVisitMap;
        }

        // 获取到今天结束的时间
        String todayEndTime = DateUtils.getToDayEndTime();
        //获取最近七天的日期
        Date sevenDaysDate = DateUtils.getDate(todayEndTime, -6);
        String sevenDays = DateUtils.getOneDayStartTime(sevenDaysDate);
        // 获取最近七天的数组列表
        List<String> sevenDaysList = DateUtils.getDaysByN(7, "yyyy-MM-dd");
        // 获得最近七天的访问量
        List<Map<String, Object>> pvMap = webVisitDao.getPVByWeek(sevenDays, todayEndTime);
        // 获得最近七天的独立用户
        List<Map<String, Object>> uvMap = webVisitDao.getUVByWeek(sevenDays, todayEndTime);

        Map<String, Object> countPVMap = new HashMap<>();
        Map<String, Object> countUVMap = new HashMap<>();

        for (Map<String, Object> item : pvMap) {
            countPVMap.put(item.get("DATE").toString(), item.get("COUNT"));
        }
        for (Map<String, Object> item : uvMap) {
            countUVMap.put(item.get("DATE").toString(), item.get("COUNT"));
        }
        // 访问量数组
        List<Integer> pvList = new ArrayList<>();
        // 独立用户数组
        List<Integer> uvList = new ArrayList<>();

        for (String day : sevenDaysList) {
            if (countPVMap.get(day) != null) {
                Number pvNumber = (Number) countPVMap.get(day);
                Number uvNumber = (Number) countUVMap.get(day);
                pvList.add(pvNumber.intValue());
                uvList.add(uvNumber.intValue());
            } else {
                pvList.add(0);
                uvList.add(0);
            }
        }
        Map<String, Object> resultMap = new HashMap<>(Constants.NUM_THREE);
        // 不含年份的数组格式
        List<String> resultSevenDaysList = DateUtils.getDaysByN(7, "MM-dd");
        resultMap.put("date", resultSevenDaysList);
        resultMap.put("pv", pvList);
        resultMap.put("uv", uvList);

        // 将一周访问量存入Redis中【过期时间10分钟】
        redisUtil.setEx(RedisConf.DASHBOARD + Constants.SYMBOL_COLON + RedisConf.WEEK_VISIT, JsonUtils.objectToJson(resultMap), 10, TimeUnit.MINUTES);
        return resultMap;
    }

    @Override
    public IPage<WebVisitEntity> getPageList(WebVisitDto webVisitDto) {
        QueryWrapper<WebVisitEntity> queryWrapper = new QueryWrapper<>();

        if (StringUtils.isNotEmpty(webVisitDto.getCreateUserUid())) {
            queryWrapper.eq(SQLConf.CREATE_USER_UID, webVisitDto.getCreateUserUid());
        }

        // 得到所有的枚举对象
        BehaviorEnum[] arr = BehaviorEnum.values();
        // 设置关键字查询
        if (!StringUtils.isEmpty(webVisitDto.getBehavior()) && !StringUtils.isEmpty(webVisitDto.getBehavior().trim())) {
            String behavior = "";
            for (int a = 0; a < arr.length; a++) {
                // 设置行为名称
                if (arr[a].getContent().equals(webVisitDto.getBehavior().trim())) {
                    behavior = arr[a].getBehavior();
                }
            }

            queryWrapper.like(SQLConf.IP, webVisitDto.getBehavior().trim()).or().eq(SQLConf.BEHAVIOR, behavior);
        }

        // 设置起始时间段
        if (!StringUtils.isEmpty(webVisitDto.getStartTime())) {
            String[] time = webVisitDto.getStartTime().split(SysConf.FILE_SEGMENTATION);
            if (time.length == 2) {
                queryWrapper.between(SQLConf.CREATE_TIME, DateUtils.str2Date(time[0]), DateUtils.str2Date(time[1]));
            }
        }

        Page<WebVisitEntity> page = new Page<>();
        page.setCurrent(webVisitDto.getCurrentPage());
        page.setSize(webVisitDto.getPageSize());
        queryWrapper.eq(SQLConf.STATUS, StatusEnum.ENABLE);
        queryWrapper.orderByDesc(SQLConf.CREATE_TIME);
        IPage<WebVisitEntity> pageList = webVisitDao.selectPage(page, queryWrapper);

        List<WebVisitEntity> list = pageList.getRecords();
        List<String> rankMemberUids = new ArrayList<>();
        List<String> rankMemberOids = new ArrayList<>();
        List<String> rankUids = new ArrayList<>();
        List<String> gradeUids = new ArrayList<>();
        List<String> catUids = new ArrayList<>();
        List<String> linkUids = new ArrayList<>();
        List<String> userUids = new ArrayList<>();

        list.forEach(item -> {

            if (StringUtils.isNotEmpty(item.getCreateUserUid())) {
                userUids.add(item.getCreateUserUid());
            }

            // 当点击博客或者点赞博客时
            if (item.getBehavior().equals(BehaviorEnum.RANK_MEMBER_CONTENT.getBehavior())
                    || item.getBehavior().equals(BehaviorEnum.RANK_MEMBER_PRAISE.getBehavior())) {
                // 从日志中提取出oid和uid
                if (StringUtils.isNotEmpty(item.getModuleUid())) {
                    rankMemberUids.add(item.getModuleUid());
                } else if (StringUtils.isNotEmpty(item.getOtherData())) {
                    rankMemberOids.add(item.getOtherData());
                }
            } else if (item.getBehavior().equals(BehaviorEnum.RANK.getBehavior()) || item.getBehavior().equals(BehaviorEnum.VISIT_CLASSIFY.getBehavior())) {
                rankUids.add(item.getModuleUid());
            }else if (item.getBehavior().equals(BehaviorEnum.RANK_CATEGORY.getBehavior()) || item.getBehavior().equals(BehaviorEnum.VISIT_CLASSIFY.getBehavior())) {
                catUids.add(item.getModuleUid());
            } else if (item.getBehavior().equals(BehaviorEnum.RANK_MEMBER_GRADE.getBehavior()) || item.getBehavior().equals(BehaviorEnum.VISIT_GRADE.getBehavior())) {
                gradeUids.add(item.getModuleUid());
            } else if (item.getBehavior().equals(BehaviorEnum.FRIENDSHIP_LINK.getBehavior())) {
                linkUids.add(item.getModuleUid());
            }
        });
        Collection<RankMemberDto> rankMemberList = new ArrayList<>();
        Collection<RankMemberDto> rankMemberListByOid = new ArrayList<>();
        Collection<RankDto> rankList = new ArrayList<>();
        Collection<MemberGradeDto> gradeList = new ArrayList<>();
        Collection<RankCategoryDto> catList = new ArrayList<>();
        Collection<LinkDto> linkList = new ArrayList<>();


        BusinessIdListDto businessIdListDto = new BusinessIdListDto();
        businessIdListDto.setRankMemberUids(rankMemberUids);
        businessIdListDto.setRankMemberOids(rankMemberOids);
        businessIdListDto.setRankUids(rankUids);
        businessIdListDto.setCatUids(catUids);
        businessIdListDto.setGradeUids(gradeUids);
        R objectResult = rankFeignClient.listBusinessObjectByIds(businessIdListDto);
        if (objectResult.getCode() == 0) {
            BusinessObjectListDto businessObjectListDto = objectResult.getData(new TypeReference<BusinessObjectListDto>(){});
            rankMemberList = businessObjectListDto.getRankMemberList();
            rankMemberListByOid = businessObjectListDto.getRankMemberListByOid();
            rankList = businessObjectListDto.getRankList();
            gradeList = businessObjectListDto.getGradeList();
            catList = businessObjectListDto.getCatList();
        }else {
            throw new FeignException("feign调用异常（根据各种业务id列表查询对应的对象集合）：失败");
        }


        List<UserEntity> userList = new ArrayList<>();
        if (userUids.size() > 0) {
            Collection<UserEntity> userCollection = userService.listByIds(userUids);
            userService.setUserAvatar(userCollection);
            userList = userService.convertUserList(userCollection);
        }

        if (linkUids.size() > 0) {
            R linkResult = adminFeignClient.lisLinktByUids(linkUids);
            if(linkResult.getCode()==0){
                linkList = linkResult.getData(new TypeReference<Collection<LinkDto>>(){});
            }else {
                throw new FeignException("feign调用异常（根据各种业务id列表查询对应的对象集合）：失败");
            }
        }


        Map<String, String> contentMap = new HashMap<>();
        rankMemberList.forEach(item -> {
            contentMap.put(item.getUid(), item.getMemberName());
        });

        rankMemberListByOid.forEach(item -> {
            contentMap.put(item.getOid() + "", item.getMemberName());
        });

        rankList.forEach(item -> {
            contentMap.put(item.getUid(), item.getRankName());
        });

        catList.forEach(item -> {
            contentMap.put(item.getUid(), item.getCatName());
        });

        gradeList.forEach(item -> {
            contentMap.put(item.getUid(), item.getGradeName());
        });

        linkList.forEach(item -> {
            contentMap.put(item.getUid(), item.getTitle());
        });

        // 用户信息
        Map<String, UserEntity> userMap = new HashMap();
        userList.forEach(item -> {
            userMap.put(item.getUid(), item);
        });

        list.forEach(item -> {
            // 设置操作人
            if (StringUtils.isNotEmpty(item.getCreateUserUid())) {
                item.setUser(userMap.get(item.getCreateUserUid()));
            }

            for (int a = 0; a < arr.length; a++) {
                // 设置行为名称
                if (arr[a].getBehavior().equals(item.getBehavior())) {
                    item.setBehaviorContent(arr[a].getContent());
                    break;
                }
            }

            if (item.getBehavior().equals(BehaviorEnum.RANK_MEMBER_CONTENT.getBehavior()) ||
                    item.getBehavior().equals(BehaviorEnum.RANK_MEMBER_PRAISE.getBehavior()) ||
                    item.getBehavior().equals(BehaviorEnum.RANK_CATEGORY.getBehavior()) ||
                    item.getBehavior().equals(BehaviorEnum.RANK.getBehavior()) ||
                    item.getBehavior().equals(BehaviorEnum.RANK_MEMBER_GRADE.getBehavior()) ||
                    item.getBehavior().equals(BehaviorEnum.VISIT_GRADE.getBehavior()) ||
                    item.getBehavior().equals(BehaviorEnum.VISIT_CLASSIFY.getBehavior()) ||
                    item.getBehavior().equals(BehaviorEnum.FRIENDSHIP_LINK.getBehavior())) {

                //从map中获取到对应的名称
                if (StringUtils.isNotEmpty(item.getModuleUid())) {
                    item.setContent(contentMap.get(item.getModuleUid()));
                } else {
                    // 从otherData中获取博客oid
                    item.setContent(contentMap.get(item.getOtherData()));
                }
            }  else if (item.getBehavior().equals(BehaviorEnum.COLLECT.getBehavior()) ||
                    item.getBehavior().equals(BehaviorEnum.RANK_PRAISE.getBehavior()) ||
                    item.getBehavior().equals(BehaviorEnum.RANK_TREAD.getBehavior())) {
                try {
                    String type = item.getOtherData();
                    ResourceTypeEnum resourceType = ResourceTypeEnum.getType(type);
                    item.setContent(resourceType.getName());
                } catch (Exception e) {
                    log.error("类型转换异常");
                }
            } else {
                item.setContent(item.getOtherData());
            }
        });
        pageList.setRecords(list);
        return pageList;
    }

    // endregion
    /***************************** didiok-admin:end **************************/



    /***************************** didiok-web:start **************************/
    // region didiok-web

    // endregion
    /***************************** didiok-web:end **************************/

    @Async
    @Override
    public void addWebVisit(String userUid, HttpServletRequest request, String behavior, String moduleUid, String otherData) {

        //增加记录（可以考虑使用AOP）
        Map<String, String> map = IpUtils.getOsAndBrowserInfo(request);
        String os = map.get("OS");
        String browser = map.get("BROWSER");
        WebVisitEntity webVisit = new WebVisitEntity();
        String ip = IpUtils.getIpAddr(request);
        webVisit.setIp(ip);

        //从Redis中获取IP来源
        String jsonResult = stringRedisTemplate.opsForValue().get("IP_SOURCE:" + ip);
        if (StringUtils.isEmpty(jsonResult)) {
            String addresses = IpUtils.getAddresses(ip, "utf-8");
            if (StringUtils.isNotEmpty(addresses)) {
                webVisit.setIpSource(addresses);
                stringRedisTemplate.opsForValue().set("IP_SOURCE" + BaseSysConf.REDIS_SEGMENTATION + ip, addresses, 24, TimeUnit.HOURS);
            }
        } else {
            webVisit.setIpSource(jsonResult);
        }
        webVisit.setOs(os);
        webVisit.setBrowser(browser);
        webVisit.setCreateUserUid(userUid);
        webVisit.setBehavior(behavior);
        webVisit.setModuleUid(moduleUid);
        webVisit.setOtherData(otherData);
        webVisit.insert();
    }

    @Override
    public Integer getVisitCountByWeek(String userUid) {
        QueryWrapper<WebVisitEntity> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq(SQLConf.CREATE_USER_UID, userUid);
        List<String> bizTypeList = new ArrayList<>();
        bizTypeList.add(ResourceTypeEnum.RANK_MEMBER.getType());
        queryWrapper.in(SQLConf.BIZ_TYPE, bizTypeList);
        Date last7Date = DateUtils.getDate(DateUtils.getNowTime(), -7);
        queryWrapper.ge(SQLConf.CREATE_TIME, last7Date);
        return webVisitDao.selectCount(queryWrapper);
    }
}